/**
 * @typedef {object} ManagerOption
 * @property {import('three').PerspectiveCamera} camera 与GLRender的共享相机
 * @property {import('three').Scene} scene  与GLRender的共享场景
 * @property {import('three').WebGLRenderer} renderer 与GLRender的共享渲染器
 * @property {string} containerId 顶层containerId
 */

import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
// import { Vector3 } from 'three';

import { h, createApp } from 'vue';
import DataCard from '@/components/DataCard.vue';
export function createVueElement(containerId, enableSelfClose = false) {
  const container = document.getElementById(containerId);
  const element = document.createElement('div');
  element.style.pointerEvents = 'auto';
  element.setAttribute('id', 'tag');
  container.appendChild(element);
  /**
   * @type {import('vue').App}
   */
  let app = null;
  const _dataCard = h(DataCard, {
    enableSelfClose,
    onClose: () => {
      // TODO:应该从场景中移除
      app && app.unmount();
    },
  });
  app = createApp(_dataCard);
  const vm = app.mount(element);
  return { element, vm };
}

/**
 * TODO:
 *   若camera相对固定/变化不大，使用CSS2D 【缩放时大小不变】
 *   若camera变化大且DOM需跟随scale变化，应该使用CSS3D + 跟随旋转的方案
 */

export default class CSSManager {
  /**
   * @type {ManagerOption}
   */
  option = {};
  /**
   * @type {Array<CSS2DObject>}
   */
  css2Objects = [];

  /**
   * @type {Array<CSS3DObject>}
   */
  CSS3DObjects = [];

  /**
   * @type {CSS2DRenderer | null}
   */
  css2DRender = null;
  /**
   * @type {CSS3DRenderer | null}
   */
  css3DRender = null;

  /**
   * @param {ManagerOption} option
   */
  constructor(option) {
    this.option = option;
    // TODO:待优化:是否启用CSS2DRenderer和CSS3DRenderer
    const containerDOM = document.getElementById(option.containerId);
    this.setCss2DRenderer(containerDOM);
    this.setCss3DRenderer(containerDOM);
  }

  /**
   * @param {HTMLElement} DOM
   */
  setCss2DRenderer(DOM) {
    const css2DRender = new CSS2DRenderer();
    // 自定义resize

    css2DRender.resize = () => {
      css2DRender.setSize(DOM.clientWidth, DOM.clientHeight);
      css2DRender.domElement.style.zIndex = 2;
      css2DRender.domElement.style.position = 'absolute';
      css2DRender.domElement.style.top = '0px';
      css2DRender.domElement.style.left = '0px';
      css2DRender.domElement.style.pointerEvents = 'none';
    };
    css2DRender.resize();
    DOM.appendChild(css2DRender.domElement);
    this.css2DRender = css2DRender;
  }

  setCss3DRenderer(DOM) {
    const css3DRender = new CSS3DRenderer();

    css3DRender.resize = () => {
      css3DRender.setSize(DOM.clientWidth, DOM.clientHeight);
      css3DRender.domElement.style.zIndex = 2;
      css3DRender.domElement.style.position = 'absolute';
      css3DRender.domElement.style.top = '0px';
      css3DRender.domElement.style.left = '0px';
      css3DRender.domElement.style.pointerEvents = 'none';
    };
    css3DRender.resize();
    DOM.appendChild(css3DRender.domElement);
    this.css3DRender = css3DRender;
  }

  /**
   * @description 不可缩放，但不会受遮挡
   * @param {object} elementOption
   * @param { HTMLElement} elementOption.element
   * @param {number} elementOption.scale
   * @param {{x: number, y: number, z: number}} elementOption.position
   */
  insertCss2DObject(elementOption) {
    const { scene } = this.option;
    const { element, position = { x: 0, y: 0, z: 0 }, scale = 1 } = elementOption;
    const mesh = new CSS2DObject(element);
    mesh.scale.set(scale, scale, scale);
    mesh.position.copy(position);
    this.css2Objects.push(mesh);
    scene.add(mesh);
    return mesh;
  }

  /**
   * @description 可缩放，但不会受遮挡
   * @param {object} elementOption
   * @param { HTMLElement} elementOption.element
   * @param {number} elementOption.scale
   * @param {{x: number, y: number, z: number}} elementOption.position
   */
  insertCss3DObject(elementOption) {
    const { scene } = this.option;
    const { element, position = { x: 0, y: 0, z: 0 }, scale = 1 } = elementOption;
    const mesh = new CSS3DObject(element);
    mesh.scale.set(scale, scale, scale);
    mesh.position.copy(position);
    this.CSS3DObjects.push(mesh);
    scene.add(mesh);
    return mesh;
  }

  // 简单实现跟随旋转
  followCameraRotate(camera) {
    this.CSS3DObjects.forEach((mesh) => {
      const direction = new Vector3();
      camera.getWorldPosition(direction);
      direction.sub(mesh.position);
      mesh.lookAt(direction);
    });
  }

  render() {
    const { scene, camera } = this.option;
    this.css2DRender.render(scene, camera);
    this.css3DRender.render(scene, camera);
    // this.followCameraRotate(camera);
  }
}
